#include "graphis_utils.h"

namespace utils{
namespace graphis{

/*********************************************
 * Bresenham直线算法 像素点绘制
 *********************************************/
// https://blog.csdn.net/u011341856/article/details/118788353
// https://www.cnblogs.com/fortunely/p/17660786.html
// (x1,y1) (x2, y2)是线段两端点
void BresenhamLine(int x1, int y1, int x2, int y2, vector<pair<int, int>>& points){
    points.clear();

    int dx = x2 - x1;
    int dy = y2 - y1;
    int stepX = dx >= 0 ? 1 : -1;
    int stepY = dy >= 0 ? 1 : -1;
    dx = abs(dx);
    dy = abs(dy);

    if (dx > dy) { // |m| < 1
        int p = 2 * dy - dx;
        int y = y1;
        for (int x = x1; x != x2; x += stepX) {
            points.emplace_back(make_pair(x, y));
            if (p > 0) {
                y += stepY;
                p -= 2 * dx;
            }
            p += 2 * dy;
        }
    }
    else { // |m| >= 1
        int p = 2 * dx - dy;
        int x = x1;
        for (int y = y1; y != y2; y += stepY) {
            points.emplace_back(make_pair(x, y));
            if (p > 0) {
                x += stepX;
                p -= 2 * dy;
            }
            p += 2 * dx;
        }
    }
}

/*********************************************
 * 基础函数
 *********************************************/
// 计算点之间的距离
double distance(const pair<double, double>& p1, 
                const pair<double, double>& p2) {
    return sqrt((p2.first - p1.first) * (p2.first - p1.first) + (p2.second - p1.second) * (p2.second - p1.second));
}

// 计算单位向量
pair<double, double> unitVector(const pair<double, double>& p1, 
                                const pair<double, double>& p2){
    double dist = distance(p1, p2);
    return {(p2.first - p1.first) / dist, (p2.second - p1.second) / dist };
}

/*********************************************
 * 计算点到直线的距离
 *********************************************/
double distancePointToLine(const pair<double, double>& p, 
                           const pair<double, double>& p1, 
                           const pair<double, double>& p2){
    // 直线的两个点 P1(x1, y1) 和 P2(x2, y2)
    double x1 = p1.first, y1 = p1.second;
    double x2 = p2.first, y2 = p2.second;
    
    // 点 P(x0, y0)
    double x0 = p.first, y0 = p.second;
    
    // 计算直线的方向向量和点到直线的向量
    double A = x2 - x1;
    double B = y2 - y1;
    double C = x0 - x1;
    double D = y0 - y1;
    
    // 使用叉积公式计算点到直线的距离
    double numerator = abs(B * C - A * D);
    double denominator = sqrt(A * A + B * B);
    
    return numerator / denominator;
}

/*********************************************
 * 计算圆与直线的交点
 *********************************************/
vector<pair<double, double>>
circleLineIntersection(const pair<double, double>& p1,
                       const pair<double, double>& p2,
                       const pair<double, double>& center, 
                       double radius){

    vector<pair<double, double>> intersectionPoints;

    // 计算单位向量
    auto e = unitVector(p1, p2);

    // 计算投影长度
    double projectionLength = ((center.first - p1.first) * (p2.first - p1.first) + (center.second - p1.second) * (p2.second - p1.second)) 
                              / distance(p1, p2);

    // 计算投影点 P_r
    pair<double, double> projection = { p1.first + projectionLength * e.first, p1.second + projectionLength * e.second };

    // 计算投影点到圆心的距离
    double d = distance(projection, center);
    
    if (d > radius) {
        // 如果投影点距离圆心超过半径，说明没有交点
        return intersectionPoints;
    } 
    else if (d == radius) {
        // 只有一个交点，即直线与圆相切
        intersectionPoints.emplace_back(projection);
        return intersectionPoints;
    }

    // 计算从投影点到交点的距离
    double baseLength = sqrt(radius * radius - d * d);

    // 计算两个交点
    pair<double, double> intersection1 = { projection.first + baseLength * e.first, projection.second + baseLength * e.second };
    pair<double, double> intersection2 = { projection.first - baseLength * e.first, projection.second - baseLength * e.second };

    intersectionPoints.emplace_back(intersection1);
    intersectionPoints.emplace_back(intersection2);

    return intersectionPoints;
}

};
}; // namespace graphis_utils